// 定义promise三种状态
const PENDING = 'PENDING',
      FULFILLED = 'FULFILLED',
      REJECTED = 'REJECTED';
function resolvePromise(promise2,x,resolve,reject){
  if(promise2 === x){
    return reject(new TypeError('Chaining cycle detected for promise #<NewPromise>'))
  }
  if((typeof x === 'object' && x !== null) || typeof x === 'function'){
    try{
      let then = x.then
      let called = false
      if(typeof then === 'function'){ //promise
        then.call(x,(y)=>{
          if(called) return
          called = true
          resolvePromise(promise2,y,resolve,reject)
        },(r)=>{
          if(called) return
          called = true
          reject(r)
        })
      }else{
        resolve(x)
      }
    }catch(e){
      if(called) return
      called = true
      reject(e)
    }
  }else{
    resolve(x)
  }
}
class NewPromise {
  constructor(execution){
    this.status = PENDING;
    this.value = undefined;
    this.reason = undefined
    this.onFulfilledCallback = []
    this.onRejectedCallback = []

    const resolve = (value)=>{
      if(this.status === PENDING){
        this.status = FULFILLED
        this.value = value
        // 发布
        this.onFulfilledCallback.map(fn => fn())
      }
    }
    const reject = (reason)=>{
      if(this.status === PENDING){
        this.status = REJECTED
        this.reason = reason
        // 发布
        this.onRejectedCallback.map(fn => fn())
      }
    }
    try{
      execution(resolve,reject)
    }
    catch(e){
      reject(e)
    }
  }
  then(onFulfilled, onRejected){
    onFulfilled = typeof onFulfilled === 'function'? onFulfilled : value => value
    onRejected = typeof onRejected === 'function'? onRejected : reason => {
      throw reason
    }

    let promise2 = new NewPromise((resolve,reject)=>{
      if(this.status === FULFILLED){
        setTimeout(() => {
          try{
            let x = onFulfilled(this.value)
            resolvePromise(promise2,x,resolve,reject)
          }catch(e){
            reject(e)
          }
        }, 0);
      }
      if(this.status === REJECTED){
        setTimeout(() => {
          try{
            let x = onRejected(this.reason)
            resolvePromise(promise2,x,resolve,reject)
          }catch(e){
            reject(e)
          }
        }, 0);
      }
      if(this.status === PENDING){
        // 订阅
        this.onFulfilledCallback.push(()=>{
          try{
            let x = onFulfilled(this.value)
            resolvePromise(promise2,x,resolve,reject)
          }catch(e){
            reject(e)
          }
        })
        this.onRejectedCallback.push(()=>{ 
          try{
            let x = onRejected(this.reason)
            resolvePromise(promise2,x,resolve,reject)
          }catch(e){
            reject(e)
          }
        })
      }
    })
    return promise2
  }
  catch(errorCallback){
    // console.log(this)
    return this.then(null,errorCallback)
  }
}

module.exports = NewPromise